using System;
using System.Collections.Generic;
using System.IO;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Measurement;
using ZoneFiveSoftware.Common.Data.Fitness;
using FilteredStatistics.Common.Controller;
using FilteredStatistics.View;

namespace FilteredStatistics.Data
{
    class MergeNearDistancesPostProcessFilter : IPostProcessFilter
    {
        public MergeNearDistancesPostProcessFilter(Stream stream, DataVersion version)
        {
            Deserialize(stream, version);
        }

        public MergeNearDistancesPostProcessFilter(float maxGapDistance, Length.Units unit)
        {
            m_MaxGapDistance = maxGapDistance;
            m_DistanceUnit = unit;
        }

#region IPostProcessFilter Members

        public override PostProcessType Type
        {
            get { return PostProcessType.MergeNearDistances; }
        }

        public override String DisplayString
        {
            get
            {
                return String.Format(PluginView.GetLocalizedString("MergeNearDistancesPPText"),
                                     m_MaxGapDistance, Length.LabelAbbr(m_DistanceUnit));
            }
        }

        public override List<ActivityRangeInfoCache> FilterResults(IList<ActivityRangeInfoCache> rangesToFilter, IActivity activity)
        {
            List<ActivityRangeInfoCache> result = new List<ActivityRangeInfoCache>();

            if (activity == null ||
                ActivityInfoCache.Instance.GetInfo(activity).MovingDistanceMetersTrack == null ||
                ActivityInfoCache.Instance.GetInfo(activity).MovingDistanceMetersTrack.Max == 0)
            {
                foreach (ActivityRangeInfoCache range in rangesToFilter)
                {
                    result.Add(range);
                }
            }
            else
            {
                bool mergedLast = false;

                for (int i = 0; i < rangesToFilter.Count - 1; ++i)
                {
                    ActivityRangeInfoCache currentRange = rangesToFilter[i];
                    ActivityRangeInfoCache nextRange = rangesToFilter[i + 1];
                    double nextStartDistance = Length.Convert(FilteredStatisticsStatisticsProvider.Instance.GetStartDistanceValue(nextRange), activity.Category.DistanceUnits, m_DistanceUnit);
                    double currentEndDistance = Length.Convert(FilteredStatisticsStatisticsProvider.Instance.GetEndDistanceValue(currentRange), activity.Category.DistanceUnits, m_DistanceUnit);

                    if (nextStartDistance - currentEndDistance > m_MaxGapDistance)
                    {
                        // Make sure not to add twice when re-evaluating
                        if (!mergedLast)
                        {
                            result.Add(currentRange);
                        }

                        mergedLast = false;
                    }
                    else
                    {
                        ActivityRangeInfoCache merged = new ActivityRangeInfoCache(new ValueRange<DateTime>(currentRange.RangeWithPauses.Lower,
                                                                                                            nextRange.RangeWithPauses.Upper),
                                                                                                            activity);

                        if (mergedLast)
                        {
                            // Overwrite, if many successive merges
                            result[result.Count - 1] = merged;
                        }
                        else
                        {
                            result.Add(merged);
                        }

                        // Setup for next attempt
                        rangesToFilter[i] = merged;
                        rangesToFilter.RemoveAt(i + 1);
                        mergedLast = true;
                        --i;
                    }
                }

                // Add the last if required
                if (!mergedLast && rangesToFilter.Count > 0)
                {
                    result.Add(rangesToFilter[rangesToFilter.Count - 1]);
                }
            }

            return result;
        }

        public override int CompareTo(IPostProcessFilter filter)
        {
            if (filter is MergeNearDistancesPostProcessFilter ||
                filter is MergeNearTimesPostProcessFilter)
            {
                return 0;
            }

            return -1;
        }

#endregion

#region IPluginSerializable members

        public override void Serialize(Stream stream)
        {
            base.Serialize(stream);

            SerializableDoubleRange maxGap = new SerializableDoubleRange(m_MaxGapDistance);
            SerializableUInt16Range unit = new SerializableUInt16Range((UInt16)m_DistanceUnit);

            maxGap.Serialize(stream);
            unit.Serialize(stream);
        }

#endregion

#region Deserialization methods

        public override void Deserialize_V2(Stream stream, DataVersion version)
        {
            SerializableDoubleRange maxGap = new SerializableDoubleRange(0);
            SerializableUInt16Range unit = new SerializableUInt16Range(0);

            maxGap.Deserialize(stream, version);
            unit.Deserialize(stream, version);

            m_MaxGapDistance = (float)maxGap;
            m_DistanceUnit = (Length.Units)(int)unit;
        }

#endregion

        public float MaxGapDistance
        {
            get { return m_MaxGapDistance; }
        }

        public Length.Units DistanceUnit
        {
            get { return m_DistanceUnit; }
        }

        private float m_MaxGapDistance;
        private Length.Units m_DistanceUnit;
    }
}
